#import lib
import plugins, cProfile, pstats
import sys, os, pkgutil, optparse

#max length to check in a file's header
HEADER_LENGTH = 10
PROFILE = 0

class SanaeEngine(object):
    '''Engine'''
    
    def __init__(self):
        
        self.parser = None
        self.path = ""
        self.obj = None
        self.outFormat = "XX"
        self.outName = ""
        self.invert = 0
        self.flipuv = 0
        self.triangulate = 0
        self.inFormat = None
        self.scale = 0
        self.separate = 0
        self.rotate = 0
        self.join_faces = 0
        self.build_verts = 0
        self.odfmod = 0
        self.objects = []
        
        self.utilities = {}
        self.plugins = {}
        self.headers = {}
        self.extensions = {}
        self.export = {}
        
    def init_optparser(self):
        '''Initial options available to the engine. Additional options may
        be added by utilities or plugins if necessary'''
        
        self.parser = optparse.OptionParser()
        self.parser.add_option("-f", dest="outFormat", help="output format")
        self.parser.add_option("-o", dest="outName", help="output filename")
        
        self.parser.add_option("-i", dest="inFormat", help="input format")
        self.parser.add_option("--rotate", default=0, help="rotate model by n degrees")
        self.parser.add_option("--scale", default=0, help="Scale model n times")
        self.parser.add_option("--join", action="store_true",
                               help="Arbitrarily join all vertices")
        self.parser.add_option("--flipuv", action="store_true",
                               help="Flip UV vertically")
        self.parser.add_option("--tri", action="store_true",
                               help="Triangulate quads")
        self.parser.add_option("--sep", action="store_true",
                               help="Separate meshes by material")
        self.parser.add_option("--build", action="store_true",
                               help="Build vertices. Used when normals or UVs"
                               "are stored with faces")
        self.parser.add_option("--odfmod", action="store_true",
                               help="Utility for ODF modding")
        self.parser.add_option("--invert", action="store_true",
                               help="Invert faces")
        
    def parse_options(self):
        
        opts, args = self.parser.parse_args()
        
        self.outFormat = opts.outFormat.upper() if opts.outFormat else "XX"
        self.outName = opts.outName if opts.outName else ""
        self.inFormat = opts.inFormat if opts.inFormat else ""
        self.invert = opts.invert
        self.flipuv = opts.flipuv
        self.scale = opts.scale
        self.triangulate = opts.tri
        self.join_faces = opts.join
        self.separate = opts.sep
        self.build_verts = opts.build
        self.odfmod = opts.odfmod
        return args
        
    def load_package_plugins(self, package):
        '''Load plugins from a specified package.'''
        ppath = package.__path__
        pname = package.__name__ + "."
        for importer, modname, ispkg in pkgutil.iter_modules(ppath, pname):
            
            module = __import__(modname, fromlist = ["dummy"])
            check = getattr(module, "definitions", None)
            if check:
                defs = module.definitions()
                headers = defs[0]
                ext = defs[1]
                name = defs[2]
                
                for idstring in headers:
                    self.headers[idstring] = module
                self.extensions[ext] = module
                self.plugins[name] = module
                self.export[ext] = module
                    
    def load_package_utilities(self, package):
        
        ppath = package.__path__
        pname = package.__name__ + "."
        for importer, modname, ispkg in pkgutil.iter_modules(ppath, pname):
            
            module = __import__(modname, fromlist = "dummy")
            check = getattr(module, "definitions", None)
            if check:
                defs = module.definitions()
                command = defs[0]
                name = defs[1]
                numArgs = defs[2]
                self.utilities[command] = module  
    
    def check_header(self, ext):
        
        ext = ext.upper()
        print self.path
        openFile = open(self.path, 'rb')
        header = openFile.read(8)
        openFile.close()

        for i in range(HEADER_LENGTH, 2, -1):
            temp = header[:i]
            if self.headers.has_key(temp):
                print self.headers[temp]
                self.obj = self.headers[temp].read_file(self.path)
                return
        if self.extensions.has_key(ext):
            self.obj = self.extensions[ext].read_file(self.path)
            return
        
        print "unrecognized format: %s" %ext
            
    def export_file(self, filename):
        
        outFile = self.outName if self.outName else filename
        if self.export.has_key(self.outFormat):
            self.export[self.outFormat].write_file(outFile, self.obj)
            print "Wrote out %s.%s" %(outFile, self.outFormat)
        else:
            print "Unrecognized export format: %s" %self.outFormat
        
    #may be inefficient to apply modifiers one at a time, but
    #it is most flexible
    def apply_mods(self):
        ''' '''
        if self.build_verts:
            self.obj.build_vertices()
        if self.triangulate:
            self.obj.triangulate_faces()
        if self.invert:
            self.obj.invert_faces()
        if self.flipuv:
            self.obj.flip_UV(self.flipuv)
        if self.scale:
            self.obj.apply_scale(self.scale)
        if self.rotate:
            self.obj.rotate_model()
        if self.join_faces:
            self.obj.join_faces()
        if self.separate:
            self.obj.separate_objects()
        
    def modify_ODF(self, args):
        
        if len(args) < 2:
            print "Error: Not enough arguments."
            print "Usage: Sanae.py --odfmod target_odf.odf your_mesh"
            exit()
        target = os.path.abspath(args[0])
        source = os.path.abspath(args[1])
        print source
        
        self.path = os.path.abspath(source)
        filename, ext = os.path.splitext(source)
        self.check_header(ext[1:])
        
        if self.obj:
            import utility.ODFMod
            utility.ODFMod.run(target, self.obj)
            
    def run(self):
        
        self.init_optparser()
        self.load_package_plugins(plugins)
        file_list = self.parse_options() 
        if self.odfmod:
            self.modify_ODF(file_list)
        else:
            for item in file_list:
                self.path = os.path.abspath(item)
                filename, ext = os.path.splitext(item)
                self.check_header(ext[1:])
                
                if self.obj:
                    self.apply_mods()
                    self.export_file(filename)
                    
    def run_blender(self, path):
        
        self.load_package_plugins(plugins)
        self.path = path
        filename, ext = os.path.splitext(os.path.basename(path))
        self.check_header(ext[1:])
        return self.obj
            
if __name__ == '__main__':
    
    engine = SanaeEngine()
    if PROFILE:
        cProfile.run("engine.run()", "res")
        p = pstats.Stats('res')
        #p.strip_dirs().sort_stats('calls')
        p.strip_dirs().sort_stats('time')
        p.print_stats()
    else:
        engine.run()
